package org.benetech.util;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Utilities for parsing string.
 * @author Reuben Firmin
 */
public class StringUtils {

	/**
	 * Non-constructor.
	 */
	private StringUtils() {

	}

	/**
	 * Count the number of occurrences of the given sequence in the given string
	 * @param string The string to examine
	 * @param sequence The sequence to look for
	 * @return 0 or more
	 */
	public static int countMatches(final String string, final String sequence) {
		return org.apache.commons.lang.StringUtils.countMatches(string, sequence);
	}

	/**
	 * Insert the given string after every match of the given pattern.
	 * @param p The pattern to find.
	 * @param string The string to insert into.
	 * @param insertText The string to insert.
	 * @return The result.
	 */
	public static String insertAfter(final Pattern p, final String string, final String insertText) {
		final Matcher matcher = p.matcher(string);
		int index = 0;
		final StringBuffer out = new StringBuffer();
		while (matcher.find(index)) {
			out.append(string.substring(index, matcher.end()));
			out.append(insertText);
			index = matcher.end();
		}
		out.append(string.substring(index, string.length()));
		return out.toString();
	}


	/**
	 * Compute the Levenshtein Distance of two strings. Levenshtein distance (LD) is a measure of the similarity between
	 * two strings, which we will refer to as the source string (s) and the target string (t). The distance is the
	 * number of deletions, insertions, or substitutions required to transform s into t.
	 * http://www.merriampark.com/ld.htm#JAVA
	 * @param s string 1
	 * @param t string 2
	 * @return the Levenshtein distance
	 */
	public static int levenshteinDistance(final String s, final String t) {
		int d[][]; // matrix
		//length of s
		int n;
		//length of t
		int m;
		//iterates through s
		int i;
		//iterates through t
		int j;
		// ith character of s
		char s_i;
		// jth character of t
		char t_j;
		// cost
		int cost;

		n = s.length();
		m = t.length();
		if (n == 0) {
			return m;
		}
		if (m == 0) {
			return n;
		}
		d = new int[n+1][m+1];
		for (i = 0; i <= n; i++) {
			d[i][0] = i;
		}
		for (j = 0; j <= m; j++) {
			d[0][j] = j;
		}
		for (i = 1; i <= n; i++) {
			s_i = s.charAt(i - 1);
			for (j = 1; j <= m; j++) {
				t_j = t.charAt(j - 1);
				if (s_i == t_j) {
					cost = 0;
				} else {
					cost = 1;
				}
				int mi;
				mi = d[i-1][j]+1;
				if (d[i][j-1]+1 < mi) {
					mi = d[i][j-1]+1;
				}
				if (d[i-1][j-1] + cost < mi) {
					mi = d[i-1][j-1] + cost;
				}
				d[i][j] =  mi;
			}
		}
		return d[n][m];
	}

	/**
	 * Determines whether the two strings are equivalent except for abbreviations, as determined by words ending by ".".
	 * For example, the strings "J. Ann McGregor" and "Joseph A. McGregor" would be equivalent. Case of characters is
	 * important.
	 * @param string1 The first string
	 * @param string2 The second string
	 * @return True if they are equal aside from abbreviations
	 */
	public static boolean equalsButAbbreviated(final String string1, final String string2) {
		String dotString1 = string1;
		String dotString2 = string2;
		// abbreviate string2 based on string1
		while (dotString1.indexOf('.') >= 0) {
			final int index = dotString1.indexOf('.');
			if (dotString2.length() > index + 1) {
				// trim the rest of the abbreviated word out of name 2
				if (dotString2.indexOf(' ', index) > 0) {
					dotString2 = dotString2.substring(0, index) + dotString2.substring(dotString2.indexOf(' ', index));
				} else {
					dotString2 = dotString2.substring(0, index);
				}
				// replace the dot in name 1
				dotString1 = dotString1.substring(0, index) + dotString1.substring(index + 1);
			} else {
				break;
			}
		}
		// abbreviate string1 based on string2
		while (dotString2.indexOf('.') >= 0) {
			final int index = dotString2.indexOf('.');
			if (dotString1.length() > index + 1) {
				// trim the rest of the abbreviated word out of name 1
				if (dotString1.indexOf(' ', index) > 0) {
					dotString1 = dotString1.substring(0, index) + dotString1.substring(dotString1.indexOf(' ', index));
				} else {
					dotString1 = dotString1.substring(0, index);
				}
				// replace the dot in name 2
				dotString2 = dotString2.substring(0, index) + dotString2.substring(index + 1);
			} else {
				break;
			}
		}
		return dotString1.equals(dotString2);
	}

    /**
     * @see org.apache.commons.lang.StringUtils#trimToNull(String)
     * @param str Null allowed.
     * @return Null when input is null or only whitespace.
     */
    public static String trimToNull(final String str)
    {
        return org.apache.commons.lang.StringUtils.trimToNull(str);
    }

    /**
     * @see org.apache.commons.lang.StringUtils#replace(String, String, String)
     * @param text Null not allowed.
     * @param repl Null not allowed.
     * @param with Null not allowed.
     * @return See {@link org.apache.commons.lang.StringUtils#replace(String, String, String)}
     */
    public static String replace(final String text, final String repl, final String with)
    {
        return org.apache.commons.lang.StringUtils.replace(text, repl, with);
    }

    /**
     * @param str Null not allowed.
     * @param start Zero or higher.
     * @param end Greater than start.
     * @return See {@link org.apache.commons.lang.StringUtils#substring(String, int, int)}
     */
    public static String substring(final String str, final int start, final int end)
    {
        return org.apache.commons.lang.StringUtils.substring(str, start, end);
    }

    /**
     * @param str Null allowed.
     * @return See {@link org.springframework.util.StringUtils#hasText(String)}.
     */
    public static boolean hasText(final String str)
    {
        return org.springframework.util.StringUtils.hasText(str);
    }

    /**
     * @param str Can be null.
     * @return See {@link org.apache.commons.lang.StringUtils#isEmpty(String)}.
     */
    public static boolean isEmpty(final String str)
    {
        return org.apache.commons.lang.StringUtils.isEmpty(str);
    }

    /**
     * @param str Can be null.
     * @param delim What to split on.
     * @return See {@link org.apache.commons.lang.StringUtils#isEmpty(String)}.
     */
    public static String[] split(final String str, final char delim)
    {
        return org.apache.commons.lang.StringUtils.split(str, delim);
    }

}
